package com.example.config;

import com.example.control.CommentControl;
import com.example.domain.t_user;
import com.example.service.IUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;

//开启MVC Security安全支持，重写用户自定义认证与授权
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private static final Logger logger= (Logger) LoggerFactory.getLogger(CommentControl.class);

    @Autowired
    private IUserService uservice;
    @Autowired
    private DataSource data;

    @Value("${COOKIE.VALIDITY}")
    private int COOKIE_VALDITY;

    /**
     * 重写方法，进行自定义用户认证
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //密码需要设置编码器
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        //进行jdbc身份认证
        String userSQL = "select username,password,valid from t_user where username=?";
        String authorSQL = "select u.username,a.authority " +
                "from t_user u,t_authority a,t_user_authority ua " +
                "where ua.user_id=u.id and ua.authority_id=a.id and u.username=?";
        auth.jdbcAuthentication().passwordEncoder(encoder).dataSource(data)
                .usersByUsernameQuery(userSQL).authoritiesByUsernameQuery(authorSQL);

    }
    //用户授权管理
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        this.logger.info("Using  configure(HttpSecurity)，用户自定义授权管理。");
        //用户自定义访问控制
        //antMatchers("/back/**","/assents/**","/user/**","/article_img/**").permitAll().
        http.authorizeRequests().
            antMatchers("/**").permitAll().
            antMatchers("/root/**").hasRole("root")
            .anyRequest().authenticated();
        //用户登陆控制
        http.formLogin().loginPage("/login")
                .usernameParameter("username").passwordParameter("password")
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request,
                          HttpServletResponse response, Authentication authentication)
                          throws IOException, ServletException {

                        //将用户放入session中
                        t_user use = uservice.Select(request.getParameter("username"));
                        HttpSession session = request.getSession();
                        session.setAttribute("loginUser",use);
                        logger.info("用户 "+use.getUsername()+" 登录在 "+request.getRemoteAddr()+" 成功");
                        String url = request.getParameter("url");
                        //获取被请求拦截的登陆原始访问路径
                        HttpSessionRequestCache cache = new HttpSessionRequestCache();
                        SavedRequest Srequest = cache.getRequest(request, response);
                        if(url!=null && !"".equals(url)&& !url.contains("register")){
                            //跳转之前的页面
                            //System.out.println(url);
                            response.sendRedirect(new URL(url).getPath());

                        }else if(Srequest!=null&&!Srequest.getRedirectUrl().contains("register")){
                            //如果存在原始拦截路径，登陆成功跳转该路径

                            response.sendRedirect(Srequest.getRedirectUrl());

                        }else{
                            //直接跳转首页
                            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
                            boolean role_admin = authorities.contains(new SimpleGrantedAuthority("ROLE_admin"));
                            if(role_admin){
                                response.sendRedirect("/admin");
                            }else{
                                response.sendRedirect("/");
                            }
                        }
                    }
                    //用户登陆失败处理
                })
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request,
                    HttpServletResponse response, AuthenticationException exception)
                            throws IOException, ServletException {
                        //登陆失败，取出原始页面url，并追加在重定向路径上
                        String url = request.getParameter("url");

                        response.sendRedirect("/login?error=用户或密码错误&url="+url);
                    }
        });
        //设置用户登陆后cookie有效期，默认值
        http.rememberMe().alwaysRemember(true).tokenValiditySeconds(COOKIE_VALDITY);
        //自定义用户退出控制
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/");
        //访问无权限页面出现的403页面进行定制处理
        http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest request,
            HttpServletResponse response, AccessDeniedException accessDeniedException)
                    throws IOException, ServletException {
                //如果是权限访问异常，进行拦截到指定错误页面
                RequestDispatcher dispatcher =
                        request.getRequestDispatcher("/errorPage/comm/error_403");
                dispatcher.forward(request,response);

            }
        });
    }
}
